﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CoreDB.Extensions
{
    public static class LinqToObjectExtension
    {
        #region ToList拍平
        public static List<T> ToList<T>(this IEnumerable<IEnumerable<T>> lilist)
        {
            return lilist.SelectMany(x => x).ToList();
            //var result = new List<T>();
            //foreach (var list in lilist)
            //    if (list.Count() > 0)
            //        result.AddRange(list);
            //return result;
        }
        /// <summary>
        /// 两层List拍平应该是够了
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lililist"></param>
        /// <returns></returns>
        public static List<T> ToList<T>(this IEnumerable<IEnumerable<IEnumerable<T>>> lililist)
        {
            return lililist.SelectMany(x => x).SelectMany(x => x).ToList();
            //var result = new List<T>();
            //foreach (var lilist in lililist)
            //{
            //    var LiList = lilist.ToList();
            //    if (LiList.Count > 0)
            //        result.AddRange(LiList);
            //}
            //return result;
        }
        #endregion

        #region KeySource
        /// <summary>
        /// 寻找序列中提供最小选择键值的元素value以及对应键值key
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="sources"></param>
        /// <param name="selector"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static KeyValuePair<TKey, TSource> MinKeySource<TSource, TKey>(this IEnumerable<TSource> sources,
            Func<TSource, TKey> selector, IComparer<TKey> comparer = null)
        {
            TSource minSource = sources.FirstOrDefault();
            if (minSource == null)
                return new KeyValuePair<TKey, TSource>();

            TKey minKey = selector(minSource);
            var targetComparer = comparer ?? Comparer<TKey>.Default;

            foreach (var source in sources)
            {
                var sourceKey = selector(source);
                if (targetComparer.Compare(minKey, sourceKey) > 0)
                {
                    minSource = source;
                    minKey = sourceKey;
                }
            }
            return new KeyValuePair<TKey, TSource>(minKey, minSource);
        }
        /// <summary>
        /// 寻找序列中提供最小选择键值的元素
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="sources"></param>
        /// <param name="selector"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static TSource MinSource<TSource, TKey>(this IEnumerable<TSource> sources,
            Func<TSource, TKey> selector, IComparer<TKey> comparer = null)
        {
            return sources.MinKeySource(selector, comparer).Value;
        }
        /// <summary>
        /// 寻找序列中提供最大选择键值的元素
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static TSource MaxSource<TSource, TKey>(this IEnumerable<TSource> sources,
            Func<TSource, TKey> selector, IComparer<TKey> comparer = null)
        {
            return sources.MaxKeySource(selector, comparer).Value;
        }

        /// <summary>
        /// 寻找序列中提供最大选择键值的元素value以及对应键值key
        /// </summary>
        /// <typeparam name="TSource"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="sources"></param>
        /// <param name="selector"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static KeyValuePair<TKey, TSource> MaxKeySource<TSource, TKey>(this IEnumerable<TSource> sources,
             Func<TSource, TKey> selector, IComparer<TKey> comparer = null)
        {
            TSource maxSource = sources.FirstOrDefault();
            if (maxSource == null)
                return new KeyValuePair<TKey, TSource>();

            TKey maxKey = selector(maxSource);
            var targetComparer = comparer ?? Comparer<TKey>.Default;

            foreach (var source in sources)
            {
                var sourceKey = selector(source);
                if (targetComparer.Compare(maxKey, sourceKey) < 0)
                {
                    maxSource = source;
                    maxKey = sourceKey;
                }
            }
            return new KeyValuePair<TKey, TSource>(maxKey, maxSource);
        }
        #endregion

        /// <summary>
        /// 返回当前序列与另一个序列中组合提供最小选择键值的元组，选择键值使用默认的比较器进行比较
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="selector">键选择器</param>
        /// <returns></returns>
        public static Tuple<T1, T2> MinWith<T1, T2, TKey>(this IEnumerable<T1> source, IEnumerable<T2> another,
            Func<T1, T2, TKey> selector, IComparer<TKey> comparer = null)
        {
            return source.MinWithKeySource(another, selector, comparer).Value;
        }

        public static KeyValuePair<TKey, Tuple<T1, T2>> MinWithKeySource<T1, T2, TKey>(this IEnumerable<T1> source, IEnumerable<T2> another,
            Func<T1, T2, TKey> selector, IComparer<TKey> comparer = null)
        {
            var tuples = source.Join(another, s => true, a => true, (s, a) => Tuple.Create(s, a));
            return tuples.MinKeySource(t => selector(t.Item1, t.Item2), comparer);
        }

        /// <summary>
        /// 返回当前序列与另一个序列中组合提供最大选择键值的元组，选择键值使用默认的比较器进行比较
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="selector">键选择器</param>
        /// <returns></returns>
        public static Tuple<T1, T2> MaxWith<T1, T2, TKey>(this IEnumerable<T1> source, IEnumerable<T2> another,
            Func<T1, T2, TKey> selector, IComparer<TKey> comparer = null)
        {
            return source.MaxWithKeySource(another, selector, comparer).Value;
        }

        public static KeyValuePair<TKey, Tuple<T1, T2>> MaxWithKeySource<T1, T2, TKey>(this IEnumerable<T1> source, IEnumerable<T2> another,
             Func<T1, T2, TKey> selector, IComparer<TKey> comparer = null)
        {
            var tuples = source.Join(another, s => true, a => true, (s, a) => Tuple.Create(s, a));
            return tuples.MaxKeySource(t => selector(t.Item1, t.Item2), comparer);
        }


        /// <summary>
        /// 将指定集合和另一个集合中的元素进行笛卡尔积，返回满足条件的第一个元组
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static Tuple<T1, T2> FirstOrDefaultWith<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> another, Func<T1, T2, bool> predicate)
        {
            var tuples = source.Join(another, s => true, a => true, (s, a) => Tuple.Create(s, a));
            return tuples.FirstOrDefault(t => predicate(t.Item1, t.Item2));
        }

        /// <summary>
        /// 将指定集合和另一个集合中的元素进行笛卡尔积，返回满足条件的所有元组
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static List<Tuple<T1, T2>> WhereWith<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> another, Func<T1, T2, bool> predicate)
        {
            var tuples = source.Join(another, s => true, a => true, (s, a) => Tuple.Create(s, a));
            return tuples.Where(t => predicate(t.Item1, t.Item2)).ToList();
        }

        /// <summary>
        /// 判断两个集合笛卡尔积后是否存在元组满足指定的谓词条件
        /// </summary>
        /// <typeparam name="T1"></typeparam>
        /// <typeparam name="T2"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static bool AnyWith<T1, T2>(this IEnumerable<T1> source, IEnumerable<T2> another, Func<T1, T2, bool> predicate)
        {
            foreach (var s in source)
                foreach (var a in another)
                    if (predicate(s, a))
                        return true;
            return false;
        }

        /// <summary>
        /// 对指定集合中的元素进行递归分组，这些组中的所有元素，两两间都满足指定谓词条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="vals"></param>
        /// <param name="func"></param>
        /// <returns></returns>
        // demo one : 平面上的一堆点中，将所有间距小于100mm的点看成一个点簇，返回所有点簇
        // demo two : 平面上的一堆多边形，将所有相互贴临的多边形合并为一个大多边形，返回所有合并后的多边形
        // demo three : 指定标高处的所有房间， 将所有二级房间合并为一级房间
        public static List<List<T>> ChainGroupBy<T>(this IEnumerable<T> vals, Func<T, T, bool> func)
        {
            var inis = vals.ToList();

            var resultList = new List<List<T>>();

            while (true)
            {
                if (inis.Count == 0)
                    break;

                resultList.Add(inis.ChainFind(inis.First(), func));
            }

            return resultList;
        }

        public static KeyValuePair<TKey, Tuple<T, T>> MinKeyDoubleSource<T, TKey>(this IEnumerable<T> sources,
            Func<T, T, TKey> selector, IComparer<TKey> comparer = null)
        {
            var sourceList = sources.ToList();
            if (sourceList.Count == 1)
            {
                var first = sourceList[0];
                return new KeyValuePair<TKey, Tuple<T, T>>(selector(first, first), Tuple.Create(first, first));
            }

            var targetComparer = comparer == null ? Comparer<TKey>.Default : comparer;
            T left = sourceList[0];
            T right = sourceList[1];
            TKey minKey = selector(left, right);
            for (int i = 0; i < sourceList.Count - 1; i++)
            {
                for (int j = i + 1; j < sourceList.Count; j++)
                {
                    var newLeft = sourceList[i];
                    var newRight = sourceList[j];
                    var newKey = selector(newLeft, newRight);
                    if (targetComparer.Compare(minKey, newKey) > 0)
                    {
                        left = newLeft;
                        right = newRight;
                        minKey = newKey;
                    }
                }
            }

            return new KeyValuePair<TKey, Tuple<T, T>>(minKey, Tuple.Create(left, right));
        }

        public static Tuple<T, T> MinDoubleSource<T, TKey>(this IEnumerable<T> sources,
            Func<T, T, TKey> selector, IComparer<TKey> comparer = null)
        {
            return sources.MinKeyDoubleSource(selector, comparer).Value;
        }

        public static KeyValuePair<TKey, Tuple<T, T>> MaxKeyDoubleSource<T, TKey>(this IEnumerable<T> sources,
            Func<T, T, TKey> selector, IComparer<TKey> comparer = null)
        {
            var sourceList = sources.ToList();
            if (sourceList.Count == 1)
            {
                var first = sourceList[0];
                return new KeyValuePair<TKey, Tuple<T, T>>(selector(first, first), Tuple.Create(first, first));
            }

            var targetComparer = comparer == null ? Comparer<TKey>.Default : comparer;
            T left = sourceList[0];
            T right = sourceList[1];
            TKey maxKey = selector(left, right);
            for (int i = 0; i < sourceList.Count - 1; i++)
            {
                for (int j = i + 1; j < sourceList.Count; j++)
                {
                    var newLeft = sourceList[i];
                    var newRight = sourceList[j];
                    var newKey = selector(newLeft, newRight);
                    if (targetComparer.Compare(maxKey, newKey) < 0)
                    {
                        left = newLeft;
                        right = newRight;
                        maxKey = newKey;
                    }
                }
            }

            return new KeyValuePair<TKey, Tuple<T, T>>(maxKey, Tuple.Create(left, right));
        }

        public static Tuple<T, T> MaxDoubleSource<T, TKey>(this IEnumerable<T> sources,
            Func<T, T, TKey> selector, IComparer<TKey> comparer = null)
        {
            return sources.MaxKeyDoubleSource(selector, comparer).Value;
        }

        /// <summary>
        /// 对两个集合进行异或操作
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static List<T> Xor<T>(this IEnumerable<T> source, IEnumerable<T> another, IEqualityComparer<T> comparer)
        {
            var intersections = source.Intersect(another, comparer);

            return source.Except(intersections, comparer).Union(another.Except(intersections, comparer)).ToList();
        }

        /// <summary>
        /// 对两个集合进行异或操作，使用默认的相等比较器
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <returns></returns>
        public static List<T> Xor<T>(this IEnumerable<T> source, IEnumerable<T> another)
        {
            var intersections = source.Intersect(another);

            return source.Except(intersections).Union(another.Except(intersections)).ToList();
        }

        public static List<T> Distinct<T>(this IEnumerable<T> source, Func<T, T, bool> comparer)
        {
            return source.Distinct(new GenericEqualityComparer<T>(comparer)).ToList();
        }

        /// <summary>
        /// 针对集合中的每一个元素，检测是否有除此之外的元素与其满足比较条件，如果满足，该元素会被过滤。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        public static List<T> Simplify<T>(this IEnumerable<T> source, Func<T, T, bool> comparer)
        {
            var sourceList = source.ToList();
            var resultList = new List<T>();
            for (int i = 0; i < sourceList.Count; i++)
            {
                int j = 0;
                for (; j < sourceList.Count; j++)
                {
                    if (j == i)
                        continue;

                    if (comparer(sourceList[i], sourceList[j]))
                        break;
                }

                if (j == sourceList.Count)
                    resultList.Add(sourceList[i]);
            }

            return resultList;
        }

        /// <summary>
        /// 返回序列的组合(数学中的排列组合)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="another"></param>
        /// <returns></returns>
        public static List<Tuple<T, T>> Combine<T>(this IEnumerable<T> source)
        {
            var resultList = new List<Tuple<T, T>>();
            var sourceList = source.ToList();

            for (int i = 0; i < sourceList.Count - 1; i++)
                for (int j = i + 1; j < sourceList.Count; j++)
                    resultList.Add(new Tuple<T, T>(sourceList[i], sourceList[j]));

            return resultList;
        }

        /// <summary>
        /// 在指定集合中内进行两两最小键值的链式迭代，并返回每次迭代组的结果集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="source"></param>
        /// <param name="selector"></param>
        /// <param name="comparer"></param>
        /// <returns></returns>
        // demo : 将一组点连成最短路径
        public static List<Tuple<T, T>> MinChainFind<T, TKey>(this IEnumerable<T> source, Func<T, T, TKey> selector, IComparer<TKey> comparer = null)
        {
            var resultList = new List<Tuple<T, T>>();
            var sourceList = source.ToList();

            if (sourceList.Count <= 1)
                return resultList;

            var initList = sourceList.GetRange(0, 1);
            var restList = sourceList.Except(initList).ToList();

            while (restList.Count != 0)
            {
                var tuple = initList.MinWith(restList, selector, comparer);
                resultList.Add(tuple);

                initList.Add(tuple.Item2);
                restList.Remove(tuple.Item2);
            }

            return resultList;
        }

        /// <summary>
        /// 从集合中取出指定开始和末尾索引的项的集合。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="startIndex"></param>
        /// <param name="endIndex"></param>
        /// <returns></returns>
        public static List<T> Take<T>(this IEnumerable<T> source, int startIndex, int endIndex)
        {
            return source.Skip(startIndex).Take(endIndex - startIndex + 1).ToList();
        }

        /// <summary>
        /// 取出集合中排除首尾指定数量的项的集合。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<T> ShrinkTake<T>(this IEnumerable<T> source, int count)
        {
            var total = source.Count();
            if (total <= count * 2)
                return new List<T>();

            return source.Take(count, total - count - 1);
        }

        /// <summary>
        /// 确定指定值是否在给定的备选列表中。
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="value"></param>
        /// <param name="vals"></param>
        /// <returns></returns>
        public static bool In<T>(this T value, params T[] vals)
        {
            return vals.ToList().Contains(value);
        }

        /// <summary>
        /// 筛选出集合中不为null的项
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <returns></returns>
        public static IEnumerable<T> WithoutNull<T>(this IEnumerable<T> source)
        {
            return source.Where(s => s != null);
        }

        #region private
        private static void ChainFindCore<T>(T val, List<T> vals, Func<T, T, bool> func, List<T> result)
        {
            if (!result.Contains(val))
                result.Add(val);
            vals.Remove(val);
            var targetVals = vals.Where(v => func(val, v)).ToList();
            foreach (var v in targetVals)
            {
                ChainFindCore(v, vals, func, result);
            }
        }
        private static List<T> ChainFind<T>(this List<T> vals, T val, Func<T, T, bool> func)
        {
            var result = new List<T>();
            ChainFindCore(val, vals, func, result);

            return result;
        }
        #endregion
    }
    public class GenericEqualityComparer<T> : IEqualityComparer<T>
    {
        public Func<T, T, bool> Comparer { get; set; }
        public GenericEqualityComparer(Func<T, T, bool> comparer)
        {
            Comparer = comparer;
        }
        public bool Equals(T x, T y)
        {
            return Comparer(x, y);
        }
        public int GetHashCode(T obj)
        {
            return 0;
        }
    }
}
